package com.vegetable.modules.service.stock.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.vegetable.common.utils.Constant;
import com.vegetable.common.utils.Constant.StockFlowType;
import com.vegetable.modules.entity.baseData.Products;
import com.vegetable.modules.entity.stock.Stock;
import com.vegetable.modules.entity.stock.StockFlow;
import com.vegetable.modules.entity.stock.po.StockFlowQueryPO;
import com.vegetable.modules.entity.stock.to.StockFlowDTO;
import com.vegetable.modules.mapper.baseData.ProductsMapper;
import com.vegetable.modules.mapper.stock.StockFlowMapper;
import com.vegetable.modules.service.stock.IStockFlowService;
import com.vegetable.modules.service.stock.IStockService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.Collator;
import java.util.*;
import java.util.stream.Collectors;


/**
 * <p>
 * 库存流水 服务类
 * </p>
 *
 * @author wangruijie
 * @since 2024-03-21
 */
@Slf4j
@Service
@AllArgsConstructor
public class StockFlowServiceImpl extends ServiceImpl<StockFlowMapper, StockFlow> implements IStockFlowService {
    @Resource
    private IStockService iStockService;
    @Resource
    private StockFlowMapper stockFlowMapper;
    @Resource
    private ProductsMapper productsMapper;

    @Override
    public IPage stockFlowList(StockFlowQueryPO po) {
        Page<StockFlow> page = new Page<>(po.getCurrent(), po.getSize());
        IPage<StockFlow> result = stockFlowMapper.selectPage(page, listQueryWrapper(po));
        List<StockFlow> flows = result.getRecords();
        List<StockFlowDTO> dtos = new ArrayList<>();
        if (CollectionUtil.isNotEmpty(flows)) {
            for (StockFlow flow : flows) {
                StockFlowDTO dto = new StockFlowDTO();
                BeanUtils.copyProperties(flow, dto);
                if (Constant.StockFlowType.isInType(flow.getOrderType())) {
                    dto.setInCost(flow.getCost());
                    dto.setInNumber(flow.getNum());
                } else {
                    dto.setOutCost(flow.getCost());
                    dto.setOutNumber(flow.getNum());
                }
                dtos.add(dto);
            }
        }
        IPage<StockFlowDTO> iPage = new Page<>();
        BeanUtils.copyProperties(result, iPage);
        iPage.setRecords(dtos);
        return iPage;
    }

    @Override
    public IPage statisticsStockFlow(StockFlowQueryPO po) {
        List<StockFlowDTO> dtos = new ArrayList<>();
        Page<Products> productIPage = new Page<>(po.getCurrent(), po.getSize());
        Map<String, Object> wrapper = new HashMap<>();
        wrapper.put("commonCode", po.getCommonCodeLike());
        wrapper.put("standardName", po.getCommodityNameLike());
        wrapper.put("categoryId", po.getCategoryId());
        IPage<Products> iPage = productsMapper.listProducts(productIPage, wrapper);
        List<Products> products = iPage.getRecords();
        List<Long> productIds = products.stream().map(el -> el.getId()).collect(Collectors.toList());
        po.setCommodityIds(productIds);
        // 如果不传查询日期，默认查询当天的
        // 查询符合条件的库存流水
        if (StrUtil.isBlank(po.getOrderTime())) {
            po.setEndTime(new Date());
        } else {
            po.setEndTime(DateUtil.parseDate(po.getOrderTime()));
        }
        po.setOrderTime(null);
        List<StockFlow> result = stockFlowMapper.selectList(listQueryWrapper(po));
        // 按照 商品编码/规格编码/日期/代办名字分组
        if (CollectionUtil.isNotEmpty(result)) {
            Map<String, List<StockFlow>> listMap = result.stream().collect(
                    Collectors.groupingBy(r -> r.getCommodityId() + "-" + r.getSpecsId()));
            for (Map.Entry<String, List<StockFlow>> en : listMap.entrySet()) {
                List<StockFlow> values = en.getValue();
                Map<Boolean, List<StockFlow>> typeGroup = values.stream().collect(Collectors.groupingBy(v -> Constant.StockFlowType.isInType(v.getOrderType())));
                List<StockFlow> inFlows = typeGroup.get(Boolean.TRUE);
                List<StockFlow> outFlows = typeGroup.get(Boolean.FALSE);
                StockFlowDTO dto = new StockFlowDTO();
                BeanUtils.copyProperties(values.get(0), dto);
                if (CollectionUtil.isNotEmpty(inFlows)) {
//                    Integer inNumber = inFlows.stream().mapToInt(t -> t.getNum()).sum();
                    BigDecimal inNumber = inFlows.stream().map(StockFlow::getNum).reduce(BigDecimal.ZERO, BigDecimal::add);
                    dto.setInStockFlows(inFlows);
                    dto.setInNumber(inNumber);
                }
                if (CollectionUtil.isNotEmpty(outFlows)) {
//                    Integer outNumber = outFlows.stream().mapToInt(t -> t.getNum()).sum();
                    BigDecimal outNumber = outFlows.stream().map(StockFlow::getNum).reduce(BigDecimal.ZERO, BigDecimal::add);
                    dto.setOutNumber(outNumber);
                    dto.setOutStockFlows(outFlows);
                }
                dto.setTempFlow(values);
                dtos.add(dto);
            }
        }
        Collections.sort(dtos, new Comparator<StockFlowDTO>() {
            final Comparator<Object> com = Collator.getInstance(java.util.Locale.CHINA);

            @Override
            public int compare(StockFlowDTO o1, StockFlowDTO o2) {
                int result = 0;
//                try {
//                    result = com.compare(o1.getOrderTime(), o2.getOrderTime());
//                }catch (Exception e){
//                    log.info("{}-----{}", o1.getOrderTime(), o2.getOrderTime());
//                    e.printStackTrace();
//                }
                if (result == 0) {
                    result = com.compare(o1.getCommodityName() + o1.getStandard(), o2.getCommodityName() + o2.getStandard());
                }
                return result;
            }
        });
        Page<StockFlowDTO> page = new Page<>();
        BeanUtils.copyProperties(productIPage, page);
        if (CollectionUtil.isNotEmpty(dtos)) {
            // 今天出入库情况
            List<StockFlowDTO> tsfd = new ArrayList<>();
            for (StockFlowDTO dto : dtos) {
                // 今天剩余的数据
                List<StockFlow> allOutFlow = dto.getTempFlow().stream().filter(el -> !Constant.StockFlowType.isInType(el.getOrderType())).collect(Collectors.toList());
                List<StockFlow> allInFlow = dto.getTempFlow().stream().filter(el -> Constant.StockFlowType.isInType(el.getOrderType())).collect(Collectors.toList());
                BigDecimal allOutNumber = allOutFlow.stream().map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal allInNumber = allInFlow.stream().map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                // 今天出入库数据
                List<StockFlow> todayOutFlow = allOutFlow.stream().filter(el -> DateUtil.isSameDay(el.getOrderTime(), po.getEndTime())).collect(Collectors.toList());
                List<StockFlow> todayInFlow = allInFlow.stream().filter(el -> DateUtil.isSameDay(el.getOrderTime(), po.getEndTime())).collect(Collectors.toList());
                BigDecimal todayOutNumber = todayOutFlow.stream().map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal todayInNumber = todayInFlow.stream().map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                // 昨天剩余情况
                List<StockFlow> yestodayOutFlow = dto.getTempFlow().stream().
                        filter(el -> !Constant.StockFlowType.isInType(el.getOrderType())).
                        filter(el -> el.getOrderTime().before(DateUtil.beginOfDay(po.getEndTime()))).
                        collect(Collectors.toList());
                List<StockFlow> yestodayInFlow = dto.getTempFlow().stream().
                        filter(el -> Constant.StockFlowType.isInType(el.getOrderType())).
                        filter(el -> el.getOrderTime().before(DateUtil.beginOfDay(po.getEndTime()))).
                        collect(Collectors.toList());
                BigDecimal yestodayOutNumber = yestodayOutFlow.stream().map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                BigDecimal yestodayInNumber = yestodayInFlow.stream().map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                dto.setInitNumber(NumberUtil.sub(yestodayInNumber, yestodayOutNumber));
                if (!NumberUtil.equals(NumberUtil.sub(yestodayInNumber, yestodayOutNumber), BigDecimal.ZERO)) {

                }
                dto.setInNumber(todayInNumber);
                dto.setInStockFlows(todayInFlow);
                dto.setOutNumber(todayOutNumber);
                dto.setOutStockFlows(todayOutFlow);
                dto.setBalanceNumber(NumberUtil.sub(allInNumber, allOutNumber));
                if (!NumberUtil.equals(NumberUtil.sub(allInNumber, allOutNumber), BigDecimal.ZERO)) {
                    List<Stock> balanceStockList = new ArrayList<>();
                    Map<Long, List<StockFlow>> batchNoMap = dto.getTempFlow().stream().collect(Collectors.groupingBy(r -> r.getStockId()));
                    for (Map.Entry<Long, List<StockFlow>> en : batchNoMap.entrySet()) {
                        Long stockId = en.getKey();
                        BigDecimal batchNoInNumber = en.getValue().stream().filter(el -> Constant.StockFlowType.isInType(el.getOrderType())).map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                        BigDecimal batchNoOutNumber = en.getValue().stream().filter(el -> !Constant.StockFlowType.isInType(el.getOrderType())).map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                        if (!NumberUtil.equals(batchNoInNumber, batchNoOutNumber)) {
                            Stock stock = new Stock();
                            BeanUtils.copyProperties(en.getValue().get(0), stock);
                            stock.setNumber(NumberUtil.sub(batchNoInNumber, batchNoOutNumber).intValue());
                            stock.setStorageOrderNo(en.getValue().get(0).getStockBatchNo());
                            stock.setId(stockId);
                            balanceStockList.add(stock);
                        }
                    }
                    dto.setBalanceStockList(balanceStockList);
                }
                if (!NumberUtil.equals(NumberUtil.sub(yestodayInNumber, yestodayOutNumber), BigDecimal.ZERO)) {
                    List<Stock> initStockList = new ArrayList<>();
                    Map<Long, List<StockFlow>> batchNoMap = dto.getTempFlow().stream().
                            filter(el -> el.getOrderTime().before(DateUtil.beginOfDay(po.getEndTime()))).
                            collect(Collectors.groupingBy(r -> r.getStockId()));
                    for (Map.Entry<Long, List<StockFlow>> en : batchNoMap.entrySet()) {
                        Long stockId = en.getKey();
                        BigDecimal batchNoInNumber = en.getValue().stream().filter(el -> Constant.StockFlowType.isInType(el.getOrderType())).map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                        BigDecimal batchNoOutNumber = en.getValue().stream().filter(el -> !Constant.StockFlowType.isInType(el.getOrderType())).map(e -> e.getNum()).reduce(BigDecimal.ZERO, BigDecimal::add);
                        if (!NumberUtil.equals(batchNoInNumber, batchNoOutNumber)) {
                            Stock stock = new Stock();
                            BeanUtils.copyProperties(en.getValue().get(0), stock);
                            stock.setNumber(NumberUtil.sub(batchNoInNumber, batchNoOutNumber).intValue());
                            stock.setStorageOrderNo(en.getValue().get(0).getStockBatchNo());
                            stock.setId(stockId);
                            initStockList.add(stock);
                        }
                    }
                    dto.setInitStockList(initStockList);
                }
                if (!NumberUtil.equals(dto.getInNumber(), BigDecimal.ZERO) || !NumberUtil.equals(dto.getOutNumber(), BigDecimal.ZERO) ||
                        !NumberUtil.equals(dto.getInitNumber(), BigDecimal.ZERO) || !NumberUtil.equals(dto.getBalanceNumber(), BigDecimal.ZERO)) {
                    tsfd.add(dto);
                }
            }
            page.setRecords(tsfd);
        }
        return page;
    }

    @Override
    public Map<String, Object> stockFlowLists(StockFlowQueryPO po) {
        Map<String, Object> result = new HashMap<>();
        StockFlow inNumber = stockFlowMapper.selectOne(inQueryWrapper(po));
        StockFlow outNumber = stockFlowMapper.selectOne(outQueryWrapper(po));
        IPage<StockFlowDTO> stockFlowList = stockFlowList(po);
        result.put("allInNumber", ObjectUtil.isNotNull(inNumber) ? inNumber.getTotalNum() : 0);
        result.put("allOutNumber", ObjectUtil.isNotNull(outNumber) ? outNumber.getTotalNum() : 0);
        result.put("stockFlowList", stockFlowList);
        return result;
    }

    @Override
    public Map<String, Object> stockFlowLists2(StockFlowQueryPO po) {
        Map<String, Object> result = new HashMap<>();
        StockFlow inNumber = stockFlowMapper.selectOne(inQueryWrapper(po));
        StockFlow outNumber = stockFlowMapper.selectOne(outQueryWrapper(po));
        IPage<StockFlowDTO> stockFlowList = stockFlowList(po);
        result.put("allInNumber", ObjectUtil.isNotNull(inNumber) ? inNumber.getTotalNum() : 0);
        result.put("allOutNumber", ObjectUtil.isNotNull(outNumber) ? outNumber.getTotalNum() : 0);
        result.put("stockFlowList", stockFlowList);
        return result;
    }

    /**
     * 查询流水列表的query
     *
     * @param po
     * @return
     */
    private QueryWrapper<StockFlow> listQueryWrapper(StockFlowQueryPO po) {
        QueryWrapper<StockFlow> queryWrapper = new QueryWrapper<>();
        // 商品名模糊查询
        extracted(po, queryWrapper);
        queryWrapper.orderByAsc("commodity_id").orderByDesc("create_time").orderByDesc("id");
        return queryWrapper;
    }

    private QueryWrapper<StockFlow> inQueryWrapper(StockFlowQueryPO po) {
        QueryWrapper<StockFlow> queryWrapper = new QueryWrapper<>();
        // 商品名模糊查询
        extracted(po, queryWrapper);
        queryWrapper.select("sum(num) as totalNum");
        queryWrapper.in("order_type", Constant.StockFlowType.inTypeList());
        return queryWrapper;
    }

    private QueryWrapper<StockFlow> outQueryWrapper(StockFlowQueryPO po) {
        QueryWrapper<StockFlow> queryWrapper = new QueryWrapper<>();
        extracted(po, queryWrapper);
        queryWrapper.select("sum(num) as totalNum");
        queryWrapper.in("order_type", Constant.StockFlowType.outTypeList());
        return queryWrapper;
    }

    private void extracted(StockFlowQueryPO po, QueryWrapper<StockFlow> queryWrapper) {
        queryWrapper.in(CollectionUtil.isNotEmpty(po.getCommodityIds()), "commodity_id", po.getCommodityIds());
        // 商品名模糊查询
        queryWrapper.like(StrUtil.isNotBlank(po.getCommodityNameLike()), "commodity_name", po.getCommodityNameLike());
        // 仓库名模糊查询
        queryWrapper.like(StrUtil.isNotBlank(po.getWarehouseNameLike()), "warehouse_name", po.getWarehouseNameLike());
        // 批次号模糊查询
        queryWrapper.like(StrUtil.isNotBlank(po.getStockBatchNoLike()), "stock_batch_no", po.getStockBatchNoLike());
        // 商品来源模糊查询
        queryWrapper.like(StrUtil.isNotBlank(po.getAgentNameLike()), "agent_name", po.getAgentNameLike());
        // 商品编码模糊查询
        queryWrapper.like(StrUtil.isNotBlank(po.getCommonCodeLike()), "common_code", po.getCommonCodeLike());
        queryWrapper.eq(StrUtil.isNotBlank(po.getStockBatchNo()), "stock_batch_no", po.getStockBatchNo());
        queryWrapper.eq(ObjectUtil.isNotNull(po.getCategoryId()), "category_id", po.getCategoryId());
        queryWrapper.eq(StrUtil.isNotBlank(po.getAgentName()), "agent_name", po.getAgentName());
        queryWrapper.eq(StrUtil.isNotBlank(po.getTargetName()), "target_name", po.getTargetName());
        queryWrapper.eq(ObjectUtil.isNotNull(po.getStandard()), "standard", po.getStandard());
        if (ObjectUtil.isNotNull(po.getStartTime())) {
            queryWrapper.between("order_time", DateUtil.beginOfDay(po.getStartTime()), DateUtil.endOfDay(po.getEndTime()));
        }
        if (ObjectUtil.isNotNull(po.getEndTime())) {
            queryWrapper.lt("order_time", DateUtil.endOfDay(po.getEndTime()));
        }
        queryWrapper.like(StrUtil.isNotBlank(po.getOrderNoLike()), "order_no", po.getOrderNoLike());
//        if(ObjectUtil.isNotNull(po.getOrderTimeEnd())){
//            queryWrapper.lt( "order_time", DateUtil.endOfDay(po.getOrderTimeEnd()));
//        }
        if (ObjectUtil.isNotNull(po.getOrderTime())) {
            queryWrapper.eq("order_time", po.getOrderTime());
        }
        queryWrapper.in(CollectionUtil.isNotEmpty(po.getOrderTypeList()), "order_type", po.getOrderTypeList());
    }


    @Override
    public List<StockFlowDTO> queryFlowByOrderNo(String orderNo) {
        StockFlowQueryPO po = new StockFlowQueryPO();
        po.setStockBatchNo(orderNo);
        po.setOrderBy("common_code");
        po.setSize(Integer.MAX_VALUE);
        IPage flows = stockFlowList(po);
        return flows.getRecords();
    }

    @Override
    public List<StockFlowDTO> queryInFlowByOrderNo(String orderNo) {
        List<StockFlowDTO> tmp = queryFlowByOrderNo(orderNo);
        List<StockFlowDTO> flows = tmp.stream().filter(en -> StockFlowType.isInType(en.getOrderType())).collect(Collectors.toList());
        return flows;
    }

    @Override
    public List<StockFlowDTO> queryOutFlowByOrderNo(String orderNo, Long orderId) {
        return this.baseMapper.queryOutFlowByOrderNo(orderNo, orderId);
    }

    @Override
    public List<StockFlowDTO> queryPackOutFlowByOrderNo(String orderNo, Long orderId) {
        return this.baseMapper.queryPackOutFlowByOrderNo(orderNo, orderId);
    }

    @Override
    public List<StockFlowDTO> queryOutFlowByOrderNo(String orderNo, Long orderId, Long detailId) {
        List<StockFlowDTO> flowDTOS = queryOutFlowByOrderNo(orderNo, orderId);
        List<StockFlowDTO> result = new ArrayList<>();
        if(CollectionUtil.isNotEmpty(flowDTOS)){
            result = flowDTOS.stream().filter(el ->el.getDetailId().equals(detailId)).collect(Collectors.toList());
        }
        return result;
    }


    @Override
    public int addStockFlow(Stock stock, Integer orderType, BigDecimal num, String orderNo, String targetName, Date orderTime) {
        StockFlow stockFlow = new StockFlow();
        BeanUtils.copyProperties(stock, stockFlow);
        // 如果修改后的数量大于现有数量，说明有盈余（5），反之系亏损（8）
        stockFlow.setOrderType(orderType);
        stockFlow.setOrderTime(orderTime);
        stockFlow.setNum(num);
        stockFlow.setSurplusStock(NumberUtil.equals(stock.getStandard(), BigDecimal.ZERO) ? stock.getWeight() : new BigDecimal(stock.getNumber()));
        stockFlow.setOrderNo(orderNo);
        stockFlow.setTargetName(targetName);
        stockFlow.setStockId(stock.getId());
        stockFlow.setCreateTime(new Date());
        stockFlow.setNumType(NumberUtil.equals(stock.getStandard(), BigDecimal.ZERO) ? 1 : 0);
        return stockFlowMapper.insert(stockFlow);
    }

    @Override
    public void deleteByOrderNo(String docNo) {
        QueryWrapper<StockFlow> wrapper = new QueryWrapper<>();
        wrapper.eq("order_no", docNo);
        stockFlowMapper.delete(wrapper);
    }
}
